#include "sw_i2c.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "flamingdeath_hardware.h"
#include "delay.h"

void i2c_init()
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* enable the clock for the LED */
  RCC_APB2PeriphClockCmd(COMP_SDA_CLK | COMP_SCL_CLK, ENABLE);

  /* set up the GPIO */
  GPIO_InitStructure.GPIO_Pin = COMP_SDA_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  /* initialize the GPIO */
  GPIO_Init(COMP_SDA_PORT, &GPIO_InitStructure);

  /* set up the GPIO */
  GPIO_InitStructure.GPIO_Pin = COMP_SCL_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  /* initialize the GPIO */
  GPIO_Init(COMP_SCL_PORT, &GPIO_InitStructure);
}

/* Hardware-Specific Support Functions That MUST Be Customized */
#define  I2CSPEED  10
void I2CDELAY(int delay) { delay_us(delay); }
unsigned READSCL(void)
{
  COMP_SCL_PORT->BSRR = COMP_SCL_PIN;
  return GPIO_ReadInputDataBit(COMP_SCL_PORT, COMP_SCL_PIN);
}  /* Set SCL as input and return current level of line, 0 or 1 */
unsigned READSDA(void)
{
  COMP_SCL_PORT->BSRR = COMP_SDA_PIN;
  return GPIO_ReadInputDataBit(COMP_SDA_PORT, COMP_SDA_PIN);
}  /* Set SDA as input and return current level of line, 0 or 1 */

void CLRSCL(void)
{
  COMP_SCL_PORT->BRR = COMP_SCL_PIN;
} /* Actively drive SCL signal low */
void CLRSDA(void)
{
  COMP_SDA_PORT->BRR = COMP_SDA_PIN;
} /* Actively drive SDA signal low */

void ARBITRATION_LOST(void)
{
  while(1);
}

/* Global Data */
unsigned start = 0;

void i2c_start_cond(void)
{
  /* if start == 1, do a restart cond */
  if (start)
  {
    /* set SDA to 1 */
    READSDA();
    I2CDELAY(I2CSPEED/2);
    /* Clock stretching */
    while (READSCL() == 0);  /* You should add timeout to this loop */
  }
  if (READSDA() == 0) 
  {
    ARBITRATION_LOST();
  }
  /* SCL is high, set SDA from 1 to 0 */
  CLRSDA();
  I2CDELAY(I2CSPEED/2);
  CLRSCL();
  start = 1;
}

void i2c_stop_cond(void)
{
  /* set SDA to 0 */
  CLRSDA();
  I2CDELAY(I2CSPEED/2);
  /* Clock stretching */
  while (READSCL() == 0);  /* You should add timeout to this loop */
  /* SCL is high, set SDA from 0 to 1 */
  if (READSDA() == 0)
  {
    ARBITRATION_LOST();
  }
  I2CDELAY(I2CSPEED/2);
  start = 0;
}

/* Write a bit to I2C bus */
void i2c_write_bit(unsigned bit)
{
  if (bit) 
  {
    READSDA();
  } 
  else 
  {
    CLRSDA();
  }
  I2CDELAY(I2CSPEED/2);
  /* Clock stretching */
  while (READSCL() == 0);  /* You should add timeout to this loop */
  /* SCL is high, now data is valid */
  /* If SDA is high, check that nobody else is driving SDA */
  if (bit)
  {
    if (READSDA() == 0) 
    {
      ARBITRATION_LOST();
    }
  }
  I2CDELAY(I2CSPEED/2);
  CLRSCL();
}

/* Read a bit from I2C bus */
unsigned i2c_read_bit(void)
{
  unsigned bit;
  /* Let the slave drive data */
  READSDA();
  I2CDELAY(I2CSPEED/2);
  /* Clock stretching */
  while (READSCL() == 0);  /* You should add timeout to this loop */
  /* SCL is high, now data is valid */
  bit = READSDA();
  I2CDELAY(I2CSPEED/2);
  CLRSCL();
  return bit;
}

/* Write a byte to I2C bus. Return 0 if ack by the slave */
unsigned i2c_write_byte(int send_start, int send_stop, unsigned char byte)
{
  unsigned bit;
  unsigned nack;
  if (send_start) 
  {
    i2c_start_cond();
  }
  for (bit = 0; bit < 8; bit++) 
  {
    i2c_write_bit(byte & 0x80);
    byte <<= 1;
  }
  nack = i2c_read_bit();
  if (send_stop) 
  {
    i2c_stop_cond();
  }
  return nack;
}

/* Read a byte from I2C bus */
unsigned char i2c_read_byte(int nak, int send_stop)
{
  unsigned char byte = 0;
  unsigned bit;
  for (bit = 0; bit < 8; bit++) 
  {
    byte <<= 1;            
    byte |= i2c_read_bit();             
  }
  i2c_write_bit(nak);
  if (send_stop) 
  {
    i2c_stop_cond();
  }
  return byte;
}
